/***********************************************************/
//模块名称：加解密
//模块说明：1）加密数据库密码
//          2）解密数据库密码
//作者：高英杰
//日期：2015-03-20
//Copyright (C), 2008-2015 Dnion Tech. Co., Ltd.
/***********************************************************/
#include "PublicCry.h"
#if USE_PUBLIC_MYSQL
#include <openssl/evp.h>
#include <openssl/md5.h>
#include <openssl/x509v3.h>
#include <openssl/ossl_typ.h>
#include <string.h>
#include <stdlib.h>
//u_char
#ifdef __MINGW32__
#include<_bsd_types.h>
#endif
/* 
 * 加密算法:
 * 加密数据库密码,通常用来生成一个密码串,
 * 用于填写到配置文件中,一般不在我们的程序中
 * 使用,用来写生成配置文件密码工具
 * 参数说明:
 * key:    加密密钥
 * in:     数据库密码
 * in_len: 密码长度
 * 返回值: 加密后的密码
 * 注:  返回值为堆上分配的内存,注意释放
 * */
char *encrypt(const char *key, const unsigned char *in, int in_len);

/* 
 * 解密算法:
 * 从配置文件中获取加密后的数据库密码,
 * 解密之后返回实际的数据库密码
 * 参数说明:
 * key:    加密密钥
 * in:     加密后的密码字符串
 * in_len: 密码字符串长度
 * 返回值: 解密后的密码,即实际的数据库密码
 * 注:  返回值为堆上分配的内存,注意释放
 * */
char *decrypt(const char *key, const unsigned char *in, int in_len);
/*后台使用key*/
#define DNIONANA_DBKEY "dn10nANa~+!_@)#($*%&^"

const char *ping_str = "dn10n+_!~";
const char *pad_str  = "cb4c04bb790d6f4cbb8e62f994d41273";

#define USE_LOCAL_BASE64 0
//EVP_EncodeBlock
//EVP_DecodeBlock
#if USE_LOCAL_BASE64
static int decode_base64_internal(unsigned char *dst, int *dst_len, unsigned char *src, int src_len, const unsigned char *basis);
unsigned char *encode_base64(unsigned char *dst, int *dst_len, const unsigned char *src, int src_len);
int decode_base64(unsigned char *dst, int *dst_len, unsigned char *src, int src_len);
#endif
//100下该函数有问题
#define USE_LOCAL_HEX 0
#if USE_LOCAL_HEX
static unsigned char *hex_to_bin(unsigned char *dst, const unsigned char *src, int len);
#endif

int makemd5key(char*key,const char*indata,int inlen){
	MD5_CTX md5_ctx;
	int i=0;
    unsigned char md[16];
	MD5_Init(&md5_ctx);
    MD5_Update(&md5_ctx, (unsigned char *)indata, inlen);
    MD5_Final(md, &md5_ctx);
	for(i = 0; i < 16; ++i) {
        snprintf(key + i * 2, 3, "%02X", md[i]);
		//printf("%02X ",md[i]);
    }
	//printf("\n");
	return 0;
}

/* key + ping_str + pad_str < 512*/
static char  *conv_key(const char *key, char *out_key){
    if(!key)return NULL;
    char new_key[512] = {0};
    int nk_len = snprintf(new_key, 512, "%s%s", key, pad_str);
    if(nk_len < 0) return NULL;
    char md5_new_key[33] = {0};
	makemd5key(md5_new_key,new_key,nk_len);	
    nk_len = snprintf(new_key, 512, "%s%s", pad_str, ping_str);
    if(nk_len < 0) return NULL;
	char str_new_key[33] = {0};
	makemd5key(str_new_key,new_key,nk_len);	
    char key_build[65] = {0};
    size_t kb_len = 0;
	int i=0;
    for(i = 0; i < 32; ++i) {
        if(!(i & 0x0001)) {
            key_build[kb_len++] = md5_new_key[i];
            key_build[kb_len++] = str_new_key[i];
        } else {
            key_build[kb_len++] = str_new_key[i];
            key_build[kb_len++] = md5_new_key[i];
        }
    }
	char long_key[33] = {0};
	makemd5key(long_key,key_build,kb_len);	
    int j = 0;
    for(i = 0; i < 32; ++i) {
        if(!(i & 0x0001)) {
            out_key[j++] = long_key[i];
        }
    }
    return out_key;
}

char *encrypt(const char *key, const unsigned char *in, int in_len)
{
    char new_key[17] = {0};
    if(!conv_key(key, new_key))
        return NULL;

    int base64_len = (((in_len + 2) / 3) * 4) + 1 + 32;
    unsigned char *base64_data = (unsigned char *)calloc(base64_len, 1);
    if(!base64_data)return NULL;
    unsigned char *encrypt_data = (unsigned char *)calloc(base64_len + 32, 1);
    if(!encrypt_data) {
        free(base64_data);
        return NULL;
    }
#if USE_LOCAL_BASE64	
    encode_base64(base64_data, &base64_len, in, in_len);
#else
	base64_len=EVP_EncodeBlock(base64_data,in,in_len);
#endif	
#if 0	
    int out_len = 0;
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
    EVP_CIPHER_CTX_set_padding(&ctx, 16);
    EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char *)new_key, NULL);  
    EVP_EncryptUpdate(&ctx, encrypt_data, &out_len, base64_data, base64_len);
    free(base64_data);

    int pad_len = 0;
    EVP_EncryptFinal_ex(&ctx, encrypt_data + out_len, &pad_len);  
    EVP_CIPHER_CTX_cleanup(&ctx);
#else
	int out_len = 0;
    EVP_CIPHER_CTX *ctx=EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);
    EVP_CIPHER_CTX_set_padding(ctx, 16);
    EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, (const unsigned char *)new_key, NULL);  
    EVP_EncryptUpdate(ctx, encrypt_data, &out_len, base64_data, base64_len);
    free(base64_data);

    int pad_len = 0;
    EVP_EncryptFinal_ex(ctx, encrypt_data + out_len, &pad_len);  
    EVP_CIPHER_CTX_free(ctx);
#endif	
    char *out = (char *)calloc((out_len + pad_len + 2) * 2 + 1, 1);
    int i = 0;
    for(i = 0; i < out_len + pad_len; ++i) {
        snprintf(out + i * 2, 3, "%02X", encrypt_data[i]);
    }
    free(encrypt_data);
    return out;
}

char *decrypt(const char *key, const unsigned char *in, int in_len)
{
    if(in_len & 0x0001)
        return NULL;
    char new_key[17] = {0};
    if(!conv_key(key, new_key))
        return NULL;
    //EVP_CIPHER_CTX ctx;
	EVP_CIPHER_CTX *ctx=EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);
    EVP_CIPHER_CTX_set_padding(ctx, 16);
    EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, (const unsigned char *)new_key, NULL); 
    int out_len = 0;
    unsigned char *decrypt_data = (unsigned char *)calloc(in_len + 32, 1);
    if(!decrypt_data) {
		EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }
#if USE_LOCAL_HEX	
    unsigned char *hex_data = (unsigned char *)calloc(in_len + 32, 1);
    if(!hex_data) {
        free(decrypt_data);
		EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }
    hex_to_bin(hex_data, in, in_len);
	EVP_DecryptUpdate(ctx, decrypt_data, &out_len, hex_data, in_len / 2);
#else
	unsigned char *hex_data=string_to_hex((char*)in,(long*)&in_len);
	EVP_DecryptUpdate(ctx, decrypt_data, &out_len, hex_data, in_len);
#endif	
    int len = 0;
    EVP_DecryptFinal_ex(ctx, decrypt_data + out_len, &len);  
    int decrypt_data_len = out_len + len;
    //EVP_CIPHER_CTX_cleanup(&ctx);
	EVP_CIPHER_CTX_free(ctx);
    char *base64_data = (char *)calloc((((decrypt_data_len + 2) / 4) * 3) + 32, 1);
    if(!base64_data) {
        free(decrypt_data);
        free(hex_data);
        return NULL;
    }
    len = 0;
#if USE_LOCAL_BASE64	
    decode_base64((unsigned char*)base64_data, &len, decrypt_data, decrypt_data_len);
#else
	len=EVP_DecodeBlock((unsigned char *)base64_data,decrypt_data, decrypt_data_len);
#endif	
    free(decrypt_data);
    free(hex_data);
    return base64_data;
}
#if USE_LOCAL_BASE64
int decode_base64_internal(unsigned char *dst, int *dst_len, unsigned char *src, int src_len, const unsigned char *basis)
{
    int          len;
    unsigned char   *d, *s;

    for (len = 0; len < src_len; len++) {
        if (src[len] == '=') {
            break;
        }

        if (basis[src[len]] == 77) {
            return -1;
        }
    }

    if (len % 4 == 1) {
        return -1;
    }

    s = src;
    d = dst;

    while (len > 3) {
        *d++ = (unsigned char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
        *d++ = (unsigned char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
        *d++ = (unsigned char) (basis[s[2]] << 6 | basis[s[3]]);

        s += 4;
        len -= 4;
    }

    if (len > 1) {
        *d++ = (unsigned char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
    }

    if (len > 2) {
        *d++ = (unsigned char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
    }

    *dst_len = d - dst;

    return 0;
}
unsigned char *encode_base64(unsigned char *dst, int *dst_len, const unsigned char *src, int src_len)
{
    unsigned char       *d;
    const unsigned char *s;
    size_t          len;
    static unsigned char   basis64[] =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    len = src_len;
    s = src;
    d = dst;

    while (len > 2) {
        *d++ = basis64[(s[0] >> 2) & 0x3f];
        *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)];
        *d++ = basis64[((s[1] & 0x0f) << 2) | (s[2] >> 6)];
        *d++ = basis64[s[2] & 0x3f];

        s += 3;
        len -= 3;
    }

    if (len) {
        *d++ = basis64[(s[0] >> 2) & 0x3f];

        if (len == 1) {
            *d++ = basis64[(s[0] & 3) << 4];
            *d++ = '=';

        } else {
            *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)];
            *d++ = basis64[(s[1] & 0x0f) << 2];
        }

        *d++ = '=';
    }
    *dst_len = d - dst;
    return dst;
}
int decode_base64(unsigned char *dst, int *dst_len, unsigned char *src, int src_len)
{
    static unsigned char   basis64[] = {
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
        77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,
        77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,

        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
        77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
    };

    return decode_base64_internal(dst, dst_len, src, src_len, basis64);
}
#endif
#if USE_LOCAL_HEX
int hextoi(const unsigned char *line, size_t n){
    unsigned char     c, ch;
    int  value;
    if (n == 0) {
        return -1;
    }
    for (value = 0; n--; line++) {
        ch = *line;
        if (ch >= '0' && ch <= '9') {
            value = value * 16 + (ch - '0');
            continue;
        }
        c = (u_char) (ch | 0x20);
        if (c >= 'a' && c <= 'f') {
            value = value * 16 + (c - 'a' + 10);
            continue;
        }
        return -1;
    }

    if (value < 0) {
        return -1;

    } else {
        return value;
    }
}
unsigned char *hex_to_bin(unsigned char *dst, const unsigned char *src, int len) 
{
    if(len & 0x0001)
        return NULL;
    int i = 0;
    for(i = 0; i < len; i+= 2) {
        *dst++ = hextoi(&src[i], 2);
    }
    return dst; 
}
#endif
bool DECRYPT(unsigned char *in, const int ilen){
	if(!in || ilen == 0) return false;
	//解密
	char *z=decrypt(DNIONANA_DBKEY, in, ilen);
	if(!z || strlen(z) == 0) {
		free(z);
		return false;
	}
	strcpy((char*)in,(char*)z);
	free(z);
	z = NULL;
	return true;;
}

bool ENCRYPT(unsigned char *in, const int ilen){
	if(!in || ilen == 0) return false;
	//解密
	char *z=encrypt(DNIONANA_DBKEY, in, ilen);
	if(!z || strlen(z) == 0) {
		free(z);
		return false;
	}
	strcpy((char*)in,(char*)z);
	free(z);
	z = NULL;
	return true;;
}

#endif
